﻿//#define DEBUG

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Collections;
using System.IO;

public class MatrixScreen : Form
{

#region Declares
    private bool debugMode = false;

    private Bitmap imgBackground;
    private Bitmap imgBackground2;
    private Bitmap imgGif;
    private Bitmap textCodeMask;
    private Bitmap bmpBuffer;
    
    private Graphics buffer;
    private Graphics primary;

    private bool useBG2 = true;
    private bool usePictures;
    private bool useTint = true;

    private int lastFrameTick;
    private int lastTick;
    private int frameRate;
    private int lastFrameRate;
    private int maxFPS;

    private string[] tehMessages;
    private string currentMessage;
    private string[] currentMessages;
    private int whichMessage = 0;
    private int currentLetter;
    private bool drawingMessage = false;
    private bool drawCarrot = true;
    private bool useMessages = true;

    private RectangleF bgRect;
    private RectangleF finalBGRect;

    private bool isNewGif = false;
    private int gifWidth;
    private int gifHeight;

    private float bgSpeed = 20;
    private bool isDone = false;

    private ArrayList codeList = new ArrayList();

    private Bitmap textCode;

    private int currentIteration = 0;
    private int iterationMax = 2;
    private int codeCount = 0;
    private int minSpeed = 10;
    private int maxSpeed = 50;

    private int letterWidth = 14;
    private int letterHeight = 16;

    private int letterCountMin = 10;
    private int letterCountMax = 25;

    private Rectangle[] brightLetters = new Rectangle[27];
    private Rectangle[] normalLetters = new Rectangle[27];
    private Rectangle[] darkLetters = new Rectangle[27];


    private ArrayList timers = new ArrayList();

    private int maxWidth;
    private int maxHeight;
    private Rectangle myBounds;
    private Point lastMousePt;

    private bool isPreview = false;
    private IntPtr parent;

    private Random rnd;

    private string[] fileList = new String[0];
    private string[] dirList;
    private string currentFileName = "";
    private bool showErrors;
    private bool includeSubs;
    private int interval;

    private EventHandler nullEvent;
    private void AnimatorCallBack(object sender, EventArgs e) { /*I know its not a declare..but make an Exception?*/ }
#endregion

#region Methods & Events
    public MatrixScreen(Rectangle screenBounds, int screenIndex, bool isPreviewWindow, IntPtr hPreviewWnd)
    {
        //Windows form defaults
        this.ShowInTaskbar = false;
        this.BackColor = Color.Black;
        this.ControlBox = false;
        this.FormBorderStyle = FormBorderStyle.None;
        
        if (debugMode)
            this.TopMost = false;
        else
            this.TopMost = true;

        this.Load += new EventHandler(this.InitScreen);
        this.MouseMove += new MouseEventHandler(MatrixScreen_MouseMove);
        this.KeyDown += new KeyEventHandler(MatrixScreen_KeyDown);
        this.KeyPreview = true;
        this.myBounds = screenBounds;
        this.Cursor.Dispose();

        //Start the render loop
        Application.Idle += new EventHandler(Application_Idle);

        //Initialize the random class. (I through the screen index in to help keep each screen different)
        rnd = new Random((screenIndex * 20) * (int)(System.DateTime.Now.Ticks % System.Int32.MaxValue));

        //Set defaults for preview window
        isPreview = isPreviewWindow;
        if (isPreview)
        {
            parent = hPreviewWnd;
            letterCountMax = 5;
            letterCountMin = 2;
            maxSpeed = 1;
            minSpeed = 1;
            iterationMax = 2;
            bgSpeed = .1f;
            this.WindowState = FormWindowState.Minimized;
        }        

        //Set max screen bounds
        maxHeight = screenBounds.Height;
        maxWidth = screenBounds.Width;
    }

    private void InitScreen(object sender, EventArgs e)
    {
        //Now that the form is loaded, set the bounds.
        this.Bounds = myBounds;

        SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
        SetStyle(ControlStyles.UserPaint, true);
        SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        SetStyle(ControlStyles.Opaque, true);
        this.UpdateStyles();

        //Initialize ImageAnimator callback
        nullEvent = new EventHandler(AnimatorCallBack);
       
        //Load our letters
        System.IO.Stream astream = this.GetType().Assembly.GetManifestResourceStream("MatrixScreen.MatrixLetters.png");
        textCode = new Bitmap(astream);
        astream.Close();

        //Mask for letters
        astream = this.GetType().Assembly.GetManifestResourceStream("MatrixScreen.MatrixLettersMask.png");
        textCodeMask = new Bitmap(astream);
        astream.Close();


        //Cache the rectangles for every letter
        int cHeight = 0;
        for (int i = 0; i < 26; i++)
        {
            darkLetters[i] = new Rectangle(0, cHeight, letterWidth, letterHeight);
            normalLetters[i] = new Rectangle(letterWidth, cHeight, letterWidth, letterHeight);
            brightLetters[i] = new Rectangle(letterWidth * 2, cHeight, letterWidth, letterHeight);
            cHeight += letterHeight;
        }

        //Retrieve saved settings
        GetSettings();

        //Initialize the timers
        InitTimers();

        //Grab the list of pictures
        FillFileList();

        //Start our first background
        if (usePictures)
            GetNewBG(rnd.Next(fileList.Length));
    }

    private void MatrixScreen_MouseMove(object sender, MouseEventArgs e)
    {
        if (!lastMousePt.IsEmpty)
        {
            //Give a little leeway for a mouse nudge
            int x = (lastMousePt.X - e.Location.X);
            int y = (lastMousePt.Y - e.Location.Y);
            
            //Distance formula
            if (Math.Sqrt(x * x + y * y) > 5)
                this.Close();
        }

        lastMousePt = e.Location;
    }

    private void MatrixScreen_KeyDown(object sender, KeyEventArgs e)
    {

        switch (e.KeyCode)
        {
            case Keys.Left:
                {
                    //TODO: Add ability to go back
                    break;
                }
            case Keys.Right:
                {
                    timerPicStay_OnTick(timers[0]);
                    break;
                }
            case Keys.Up:
                { 
                    timerSanchez_OnTick(timers[3]);
                    break;
                }
            default:
                {
                    this.Close();
                    break;
                }
        }
            
    }

    private void GetSettings()
    {
        Microsoft.Win32.RegistryKey settings = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\MatrixShow");

        if (settings != null)
        {            
            showErrors = Convert.ToBoolean(settings.GetValue("showerr", false));
            useTint = Convert.ToBoolean(settings.GetValue("usetint", true));
            includeSubs = Convert.ToBoolean(settings.GetValue("includesub", true));
            usePictures = Convert.ToBoolean(settings.GetValue("showpics", true));
            interval = Convert.ToInt32(settings.GetValue("interval", 15)) * 1000;
            maxFPS = Convert.ToInt32(settings.GetValue("maxFPS", 60));
            useMessages = Convert.ToBoolean(settings.GetValue("showMsgs", true));

            debugMode = Convert.ToBoolean(settings.GetValue("debug", false));

            dirList = (string[])settings.GetValue("folders", Environment.GetFolderPath(Environment.SpecialFolder.MyPictures));

            tehMessages = (string[])settings.GetValue("messages", ConfigMessages.defaultMsgs);
            if (tehMessages.Length == 0)
                tehMessages = ConfigMessages.defaultMsgs;
        }
        else
        {
            showErrors = false;
            useTint = true;
            includeSubs = true;
            usePictures = true;
            interval = 15000;
            maxFPS = 60;
            useMessages = true;

            debugMode = false;

            dirList = new string[] { Environment.GetFolderPath(Environment.SpecialFolder.MyPictures) };

            tehMessages = (string[])settings.GetValue("messages", ConfigMessages.defaultMsgs);
        }

        //In the preview box we just want to see it.
        if (isPreview)
            interval = 5000;

        //Make sure we had a directory
        if (dirList.Length == 0)
            dirList = new string[] { Environment.GetFolderPath(Environment.SpecialFolder.MyPictures) };
    }

    private void FillFileList()
    {
        string[] supportedFormats = new string[] {".bmp", ".jpg", ".jpeg", ".png", ".gif", ".exig", ".tiff" };
        
        ArrayList tmpFiles = new ArrayList();
        FileInfo[] files;
        DirectoryInfo dir;
        string[] dirs;

        //For each directory in our list
        foreach (string picDir in dirList)
        {
            if (Directory.Exists(picDir))
            {
                dir = new DirectoryInfo(picDir);

                //Enum each file format in the root directory
                foreach (string format in supportedFormats)
                {
                    files = dir.GetFiles("*" + format);

                    foreach (FileInfo file in files)
                    {
                        //Add it to our list
                        tmpFiles.Add(file.FullName);
                    }
                }

                if (includeSubs)
                {
                    //Get all subdirectorys
                    dirs = Directory.GetDirectories(picDir, "*", SearchOption.AllDirectories);

                    //Enum each file format in each sub directory
                    foreach (String sDir in dirs)
                    {
                        dir = new DirectoryInfo(sDir);

                        foreach (string format in supportedFormats)
                        {
                            files = dir.GetFiles("*" + format);

                            foreach (FileInfo file in files)
                            {
                                tmpFiles.Add(file.FullName);
                            }
                        }
                    }
                }
            }
        }

        //Our file list.
        fileList = (string[])tmpFiles.ToArray(typeof(string));

        if (fileList.Length < 1)
            usePictures = false;
    }

    //I am lazy.
    //http://stackoverflow.com/questions/921180/c-round-up
    private int RoundUp(int dividend, int divisor)
    {
        int roundedTowardsZeroQuotient = dividend / divisor;
        bool dividedEvenly = (dividend % divisor) == 0;
        if (dividedEvenly)
            return roundedTowardsZeroQuotient;

        bool wasRoundedDown = ((divisor > 0) == (dividend > 0));
        if (wasRoundedDown)
            return roundedTowardsZeroQuotient + 1;
        else
            return roundedTowardsZeroQuotient;
    }
#endregion

#region Render Loop
    //Thank you Tom Miller.
    private void Application_Idle(object sender, EventArgs e)
    {
        while (AppStillIdle)
        {
            GC.Collect();

            if (!((Environment.TickCount - lastFrameTick) < (1000 / maxFPS)))
            {
                DoUpdate(Environment.TickCount);
                DoRender();
                frameRate++;
                if (lastFrameTick == 0)
                    lastFrameTick = Environment.TickCount;
                lastFrameTick += (1000 / maxFPS);
            }

            if (Environment.TickCount - lastTick > 1000)
            {
                lastTick = Environment.TickCount;
                lastFrameRate = frameRate;
                frameRate = 0;
            }

            if (isPreview)
                if (!User32.IsWindowVisible(parent))
                    this.Close();
        }
    }

    private bool AppStillIdle
    {
        get
        {
            Message msg;
            return !User32.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
        }
    }

    private void DoUpdate(int currentTime)
    {
        if (usePictures)
        {
            //Update background slide
            //If we're not on the screen or we're done showing this one
            if (bgRect.Y < finalBGRect.Y || isDone)
                bgRect.Y += bgSpeed;
            else
            {
                //We've centered on the screen, start the interval clock
                //Only enable the timer once
                if (((Timer)timers[0]).Enabled == false)
                {
                    ((Timer)timers[0]).Enabled = true;
                }
            }

            //Fell off the screen start a new picture
            if (bgRect.Y > maxHeight + 600 && isDone)
            {
                isDone = false;
                ((Timer)timers[2]).Enabled = false;
                StartNewPic();
            }
        }

        //Update code pics locations.
        foreach (MatrixCode mCode in codeList)
        {
            mCode.Y += mCode.Speed;

            if (mCode.Y > maxHeight)
                mCode.Location = new PointF(mCode.X, -maxHeight);

            //Check for collision
            if (usePictures)
                mCode.IntersectRect = RectangleF.Intersect(mCode.Bounds, bgRect);
        }

        //Update timers
        foreach (Timer timer in timers)
        {
            timer.UpdateTimer(currentTime);
        }
    }

    private void DoRender()
    {
        //Allocate our buffer bitmap
        bmpBuffer = new Bitmap(maxWidth, maxHeight, PixelFormat.Format32bppPArgb);

        //Our we drawing to the preview window?
        if (!isPreview)
            primary = this.CreateGraphics();
        else
            primary = Graphics.FromHwnd(parent);

        //Start up our buffer
        IntPtr hdc = primary.GetHdc();
        IntPtr hMemdc = GDI32.CreateCompatibleDC(hdc);
        IntPtr bufptr = bmpBuffer.GetHbitmap();

        GDI32.SelectObject(hMemdc, bufptr);
        buffer = Graphics.FromHdc(hMemdc);

        //with a clean slate..
        buffer.Clear(Color.Black);

        //The rectangle we will be drawing in
        RectangleF drawRect = RectangleF.Empty;

        if (!drawingMessage)
        {
            foreach (MatrixCode mCode in codeList)
            {
                //Draw the code
                buffer.DrawImage(mCode.CodePic, mCode.Location);

                if (usePictures)
                {
                    //If we intersected during update..
                    if (mCode.IntersectRect != Rectangle.Empty)
                    {
                        //Offset our rectangle to our current location 
                        drawRect.X = mCode.X - bgRect.Left;
                        drawRect.Y = mCode.IntersectRect.Y - bgRect.Y;
                        drawRect.Width = mCode.IntersectRect.Width;
                        drawRect.Height = mCode.IntersectRect.Height;

                        //Draw the appropiate BG
                        if (!useBG2)
                            buffer.DrawImage(imgBackground, mCode.IntersectRect, drawRect, GraphicsUnit.Pixel);
                        else
                            buffer.DrawImage(imgBackground2, mCode.IntersectRect, drawRect, GraphicsUnit.Pixel);

                        //Draw the mask over it
                        buffer.DrawImage(mCode.CodePicMask, mCode.Location);
                    }
                }
            }
        }
        else
        { 
            Font fnt = new Font("System", 18, FontStyle.Bold);
            SizeF msgSize = buffer.MeasureString(currentMessages[currentLine], fnt);

            for (int i = 0; i <= currentLine; i++)
            {
                buffer.DrawString(currentMessages[i], fnt, Brushes.Green, 0, (i * msgSize.Height));
            }
            
            if (drawCarrot)
            {
                Int32 carrotX = (Int32)msgSize.Width + 5;
                buffer.DrawLine(Pens.Green, carrotX, (currentLine * msgSize.Height + 5), carrotX, ((currentLine + 1) * msgSize.Height - 5));
            }

            fnt.Dispose();
        }

        if (debugMode)
        {
            //Show pic rectangle
            buffer.DrawRectangle(Pens.Red, Rectangle.Round(bgRect));

            Font fnt = new Font(FontFamily.GenericSansSerif, 30);

            //Show frame rate        
            buffer.DrawString(lastFrameRate.ToString(), fnt, Brushes.Blue, 0, 0);

            if (currentFileName != "")
                buffer.DrawString(currentFileName, fnt, Brushes.Blue, 0, maxHeight - 60);

            fnt.Dispose();
        }

        //Flip the buffer using native BitBlt(its a tad faster than DrawImage)
        GDI32.BitBlt(hdc, 0, 0, maxWidth, maxHeight, hMemdc, 0, 0, 13369376);

        //Clean up
        GDI32.DeleteDC(hdc);
        GDI32.DeleteDC(hMemdc);
        GDI32.DeleteObject(bufptr);

        primary.Dispose();
        buffer.Dispose();
        bmpBuffer.Dispose();
    }
#endregion

#region Timers
    private void InitTimers()
    {
        //0 How long the pic stays on the screen
        Timer timerPicStay = new Timer(interval);
        timerPicStay.OnTick += new Timer.OnTickEvent(timerPicStay_OnTick);
        timerPicStay.Enabled = false;
        timers.Add(timerPicStay);

        //1 This helps spread out the falling code
        Timer startTimer = new Timer(50);
        startTimer.OnTick += new Timer.OnTickEvent(startTimer_OnTick);
        timers.Add(startTimer);

        //2 Our GIF animator
        Timer timerGifUpdate = new Timer(150);
        timerGifUpdate.OnTick += new Timer.OnTickEvent(timerGifUpdate_OnTick);
        timerGifUpdate.Enabled = false;
        timers.Add(timerGifUpdate);

        //3 The Matrix Has You...
        Timer timerSanchez = new Timer(rnd.Next(30000, 300000));
        timerSanchez.OnTick += new Timer.OnTickEvent(timerSanchez_OnTick);
        timerSanchez.Enabled = useMessages;
        timers.Add(timerSanchez);

        //4 Type the message
        Timer timerIHasYou = new Timer(200);
        timerIHasYou.OnTick += new Timer.OnTickEvent(timerIHasYou_OnTick);
        timerIHasYou.Enabled = false;
        timers.Add(timerIHasYou);

        //5 Carrot Timer
        Timer timerGoku = new Timer(350);
        timerGoku.OnTick += new Timer.OnTickEvent(timerGoku_OnTick);
        timerGoku.Enabled = false;
        timers.Add(timerGoku);
    }

    private void timerGoku_OnTick(object sender)
    {
        drawCarrot = !drawCarrot;
    }

    int currentLine;
    private void timerIHasYou_OnTick(object sender)
    {
        if (currentLetter % 80 == 0 && currentLetter != 0 && currentLetter < currentMessage.Length)
            currentLine++;

        if (currentLetter < tehMessages[whichMessage].Length)
            currentMessages[currentLine] = tehMessages[whichMessage].Substring(currentLine * 80, currentLetter - (currentLine * 80) + 1);

        currentLetter++;

        if (currentLetter > currentMessage.Length + 40)
        {
            ((Timer)sender).Enabled = false;
            ((Timer)timers[5]).Enabled = false;
            drawingMessage = false;
            ((Timer)timers[3]).Interval = rnd.Next(30000, 300000);
            ((Timer)timers[3]).Enabled = true;
        }
    }

    private void timerSanchez_OnTick(object sender)
    {
        //Start it
        ((Timer)sender).Enabled = false;
        ((Timer)timers[4]).Enabled = true;
        ((Timer)timers[5]).Enabled = true;

        whichMessage = rnd.Next(tehMessages.Length);
        currentMessage = tehMessages[whichMessage];
        currentMessages = new String[RoundUp(currentMessage.Length, 80)];

        for (int i = 0; i < currentMessages.Length; i++)
            currentMessages[i] = "";

        currentLetter = 0;
        drawingMessage = true;
    }

    private void timerGifUpdate_OnTick(object sender)
    {
        //Multi threading ftw.
        System.Threading.Thread t = new System.Threading.Thread(ApplyGifFilter);
        t.Start();
    }

    private void ApplyGifFilter()
    {
        //Have to draw the new frame before appling filters
        Bitmap b = new Bitmap(gifWidth, gifHeight);
        Graphics g = Graphics.FromImage(b);
        bool skipIt = false;

        try
        {
            ImageAnimator.UpdateFrames();
            g.DrawImage(imgGif, 0, 0);
        }
        catch 
        {
            skipIt = true;
            b.Dispose();
        }

        g.Dispose();

        if (!skipIt)
        {
            b = ApplyFilters(ref b);
            SetImage(b);
        }
    }

    private void startTimer_OnTick(object sender)
    {
        //If we haven't reached the end of the screen
        if (letterWidth * (codeCount) < maxWidth)
        {
            //Create a new code pic
            MatrixCode mCode = new MatrixCode();
            MatrixCode mCode2 = new MatrixCode();

            //Get a random number of letters
            int numLetters = rnd.Next(letterCountMin, letterCountMax);
            mCode.LetterArray = new int[numLetters];

            //for each letter slot get a random letter
            for (int i = 0; i < numLetters; i++)
            {
                mCode.LetterArray[i] = rnd.Next(25);
            }

            //Create our bitmap and mask
            Bitmap nBitmap = new Bitmap(letterWidth, letterHeight * numLetters, PixelFormat.Format32bppPArgb );
            Bitmap nMask = new Bitmap(letterWidth, letterHeight * numLetters, PixelFormat.Format32bppPArgb);
            Graphics g = Graphics.FromImage(nBitmap);
            Graphics gMask = Graphics.FromImage(nMask);
           

            //This snippet decides how many dark letters and how many light letters we see.
            int cY = 0;
            
            for (int i = 0; i < numLetters / 4; i++)
            {
                g.DrawImage(textCode, mCode.X, cY, darkLetters[mCode.LetterArray[i]], GraphicsUnit.Pixel);
                gMask.DrawImage(textCodeMask, mCode.X, cY, brightLetters[mCode.LetterArray[i]], GraphicsUnit.Pixel);
                cY += letterHeight;
            }

            numLetters = (numLetters - 1) - (numLetters / 4);

            if (numLetters < 0)
                numLetters = 3;

            for (int i = 0; i < numLetters; i++)
            {
                g.DrawImage(textCode, mCode.X, cY, normalLetters[mCode.LetterArray[i]], GraphicsUnit.Pixel);
                gMask.DrawImage(textCodeMask, mCode.X, cY, brightLetters[mCode.LetterArray[i]], GraphicsUnit.Pixel);
                cY += letterHeight;
            }

            //Make our last letter a bright letter
            int lastLetter = rnd.Next(25);

            g.DrawImage(textCode, mCode.X, cY, brightLetters[lastLetter], GraphicsUnit.Pixel);
            gMask.DrawImage(textCodeMask, mCode.X, cY, brightLetters[lastLetter], GraphicsUnit.Pixel);

            mCode.CodePic = nBitmap;
            mCode.CodePicMask = nMask;

            mCode2.CodePic = nBitmap;
            mCode2.CodePicMask = nMask;

            //Set the new location
            mCode.Location = new Point(letterWidth * codeCount, -maxHeight);

            //Added this second one to help start up quicker
            mCode2.Location = new Point(rnd.Next(maxWidth), -maxHeight);

            //If we are in preview mode slow down the code
            //Otherwise get a random speed
            if (!isPreview)
            {
                mCode.Speed = (float)rnd.Next(minSpeed, maxSpeed);
                mCode2.Speed = (float)rnd.Next(minSpeed, maxSpeed);
            }
            else
            {
                mCode.Speed = .3f;
                mCode2.Speed = .3f;
            }

            //Add it to our code list
            codeList.Add(mCode);
            codeList.Add(mCode2);

            codeCount++;

            //Clean up
            g.Dispose();
            gMask.Dispose();
        }
        else
        {
            //If we reached the end of the screen
            //lets start over to help fill the screen with code.
            if (currentIteration < iterationMax)
            {
                codeCount = 0;
                currentIteration++;
            }
            else
                ((Timer)sender).Enabled = false;
        }
    }

    private void timerPicStay_OnTick(object sender)
    {
        //The picture had run its course..set isDone so it will fall away
        ((Timer)sender).Enabled = false;
        isDone = true;
    }
#endregion

#region Background Image Methods
    private void StartNewPic()
    {
        //Use a different thread to apply filters to background image.
        //Helps to avoid the hiccup between pictures
        System.Threading.Thread t = new System.Threading.Thread(GetNewBG);
        t.Start(rnd.Next(fileList.Length));
    }

    private void SetImage(Bitmap b)
    {
        int cWidth = b.Width;
        int cHeight = b.Height;

        //Using two seprate backgrounds helps avoid writing the 
        //new picture to memory while our loop is trying to draw it.
        if (useBG2)
            imgBackground = b;
        else
            imgBackground2 = b;    


        //Create the start and finish rectangles
        if (((Timer)timers[2]).Enabled == false || isNewGif)
        {
            isNewGif = false;
            bgRect = new Rectangle((maxWidth / 2) - (cWidth / 2), maxHeight - maxHeight - cHeight, cWidth, cHeight);
            finalBGRect = new Rectangle((maxWidth / 2) - (cWidth / 2), (maxHeight / 2) - (cHeight / 2), cWidth, cHeight);
        }

        //Flip which background we are drawing
        useBG2 = !useBG2;
    }

    private void GetNewBG(object oNextIndex)
    {
        Bitmap imgNewBG = null;
        Bitmap tmpBmp = null;

        //Our random index coming in
        int nextIndex = (int)oNextIndex;

        //So we can get the file name
        string[] tmp = fileList[nextIndex].Split(new char[] { '\\' });
        currentFileName = tmp[tmp.Length - 1];
        string fileExt = currentFileName.Substring(currentFileName.LastIndexOf('.') + 1);

        try
        {

            //Load the new bitmap
            imgNewBG = new Bitmap(fileList[nextIndex]);

            //Static pic
            if (!ImageAnimator.CanAnimate(imgNewBG))
            {
                imgNewBG = ApplyFilters(ref imgNewBG);
                ((Timer)timers[2]).Enabled = false;
            }
            else
            {
                //Set bitmap to our gif holder and animate it
                isNewGif = true;
                imgGif = imgNewBG;
                gifWidth = imgGif.Width;
                gifHeight = imgGif.Height;
                ImageAnimator.Animate(imgGif, nullEvent);
                imgNewBG = ApplyFilters(ref imgNewBG);
                ((Timer)timers[2]).Enabled = true;
            }

        }
        catch (Exception e)
        {
            //We had an error loading the bitmap
            tmpBmp = new Bitmap(maxWidth, maxHeight, PixelFormat.Format32bppPArgb);
            Graphics g = Graphics.FromImage(tmpBmp);

            //Our err font
            Font fnt = new Font(FontFamily.GenericSansSerif, 42, FontStyle.Bold);

            if (showErrors)//Maybe we dont care about errors
            {
                g.DrawString(e.Message, fnt, Brushes.Blue, 0, 0, StringFormat.GenericDefault);
                g.DrawString(currentFileName, fnt, Brushes.Blue, 0, 50, StringFormat.GenericDefault);
            }
            else
                g.Clear(Color.Transparent);

            g.Dispose();
            imgNewBG = (Bitmap)tmpBmp.Clone();
        }

        //Set our backgound image to our new image
        SetImage(imgNewBG);

        //clean up if need be
        if (tmpBmp != null)
            tmpBmp.Dispose();

    }

    public Bitmap ApplyFilters(ref Bitmap b)
    {
        //Maintain aspect ratio
        float widthPer;
        float heightPer;
        int nWidth = maxWidth;
        int nHeight = maxHeight;

        int orgWidth = b.Width;
        int orgHeight = b.Height;

        if (orgWidth > orgHeight)
        {
            widthPer = (float)nWidth / orgWidth;
            nHeight = (int)(orgHeight * widthPer);
        }
        else
        {
            heightPer = (float)nHeight / orgHeight;
            nWidth = (int)(orgWidth * heightPer);
        }

        Bitmap result = new Bitmap(nWidth, nHeight, PixelFormat.Format32bppPArgb);
        Graphics g = Graphics.FromImage(result);
        ImageAttributes ia = new ImageAttributes();
        ColorMatrix cm = new ColorMatrix();

        //Tint and set opacity
        cm.Matrix00 = 1;
        cm.Matrix11 = 1;
        cm.Matrix22 = 1;
        cm.Matrix33 = .75f;//1;//opacity;
        if (useTint)
            cm.Matrix41 = 100f / 255f;
        cm.Matrix44 = 1;

        ia.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

        g.DrawImage(b, new Rectangle(0, 0, nWidth, nHeight), 0, 0, orgWidth, orgHeight, GraphicsUnit.Pixel, ia);
        g.Dispose();

        return result;
    }
#endregion
}

